package eu.fritschi.parachatcapture;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.gtranslate.Audio;
import com.gtranslate.Language;

/**
 * This class uses code from http://code.google.com/p/java-google-translate-text-to-speech/
 * to do the actual text to speech bit. The only thing it does besides that is splitting up messages
 * that are too large to work with google translate restful api and feeding it bit
 * by bit into the code.
 *
 */
public class GoogleTranslateTextToSpeech {
	private static final Logger logger = Logger.getLogger(GoogleTranslateTextToSpeech.class);
	
	private final Audio audio;
	private final Set<Character> blockEnders;
	private final int maxBlockLength;
	private final Map<String, Long> authorsLastMessageTimestamps = new HashMap<String, Long>();
	private String lastAuthorThatSpoke = null;
	
	public GoogleTranslateTextToSpeech() {
		audio = Audio.getInstance();
		blockEnders = new HashSet<Character>();
		blockEnders.add('.');
		blockEnders.add(' ');
		blockEnders.add('!');
		blockEnders.add('?');
		maxBlockLength = 90;
	}

	public void say(String message) {
		List<String> messageBlocks = new ArrayList<String>();
		String remainingMessage = message;
		while (remainingMessage.length() > maxBlockLength) {
			int blockEnd = -1;
			for (int i = maxBlockLength - 1; i >= 0; i--) {
				Character c = remainingMessage.charAt(i);
				if (blockEnders.contains(c)) {
					blockEnd = i;
					break;
				}
			}
			if (blockEnd == -1) {
				blockEnd = maxBlockLength -1;
			}
			messageBlocks.add(remainingMessage.substring(0, blockEnd + 1));
			remainingMessage = remainingMessage.substring(blockEnd + 1);
		}
		if (remainingMessage.length() > 0) {
			messageBlocks.add(remainingMessage);
		}

		for (String string : messageBlocks) {
			InputStream sound;
			try {
				sound = audio.getAudio(string, Language.ENGLISH);
				audio.play(sound);					
			} catch (Exception e) {
				logger.error("Failed to say: " + message);
			}
		}
	}

	public void speakMessage(ParachatMessage parachatMessage) {
		String author = parachatMessage.getAuthor();
		long timestampOfCurrentMessage = parachatMessage.getTimestampInUtc();
		// Check if previous message was from the same author.
		if (author.equals(lastAuthorThatSpoke)) {
			// Check if the author has spoken within the last 10 seconds.
			Long timeStampOfPrevious = authorsLastMessageTimestamps.get(author);
			if (timeStampOfPrevious == null || (timestampOfCurrentMessage - timeStampOfPrevious) > 15000 ) {
				// Author did not speak up within the last ten seconds. Announce his name.
				say(author);
			} // else don't say the name otherwise it can be quite annoying if the name is read
			// for each separate message if the author writes multiple messages in quick succession.
			
		} else {
			say(author);
		}
		say(parachatMessage.getMessage());
		
		authorsLastMessageTimestamps.put(author, timestampOfCurrentMessage);
		lastAuthorThatSpoke = author;
	}
}
